package com.tools.web.interceptor.auth;

import com.tools.common.object.json.JsonKit;
import com.tools.common.object.Note;
import com.tools.common.thread.Timeunit;
import com.tools.web.interceptor.access.AccessManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 使用 RedisTemplate 操作 Redis 客户端
 * 完成用户认证功能的管理器
 * */
@Note("使用 RedisTemplate 操作 Redis 客户端" +
        "完成用户认证功能的管理器")
public final class SingleRedisTemplateAuthenticationManager extends GenericAuthenticationManager {

    @Note("使用 JedisPlus 操作 Redis 单机客户端完成用户认证等操作")
    private final RedisTemplate<String, String> redisTemplate;

    @Note("日志对象")
    private static final Logger LOGGER = LoggerFactory.getLogger(SingleRedisTemplateAuthenticationManager.class);

    /* **************************************************************************************
     *
     *          构造方法
     *
     * **************************************************************************************
     * */

    public SingleRedisTemplateAuthenticationManager(RedisTemplate<String, String> redisTemplate, AuthSource source,
                                                    AuthUserService authUserService) {
        this(redisTemplate, source, authUserService, null);
    }

    public SingleRedisTemplateAuthenticationManager(RedisTemplate<String, String> redisTemplate, AuthSource source,
                                                    AuthUserService authUserService, AccessManager accessManager) {
        super(source, authUserService, accessManager);
        this.redisTemplate = Objects.requireNonNull(redisTemplate);
    }

    /* **************************************************************************************
     *
     *          功能方法
     *
     * **************************************************************************************
     * */

    @Override
    protected Logger protected_getLogger() {
        return LOGGER;
    }

    @Override
    protected Timeunit protected_getTimeunitToBeUsed() {
        return Timeunit.SECOND;
    }

    @Override
    protected void protected_loginOut(String key, String deviceTypeId, String uuid,
                                      AuthUser authUser, String separator) {
        this.removeAuthUser(key);
    }

    @Override
    protected Res getAuthUser(String key, AuthToken token) {
        String json = this.redisTemplate.opsForValue().get(key);
        if(json == null) return super.protected_getResIfExpired(token);
        SingleAuthUser authUser = JsonKit.toEntity(json, SingleAuthUser.class);
        if(Objects.equals(authUser.getUuid(), token.getUuid()) &&
                Objects.equals(authUser.getDeviceTypeId(), token.getDeviceTypeId())) {
            return new Res(true, authUser);
        }
        return new Res(false, null);
    }

    @Override
    protected void setAuthUser(String key, AuthUser srcAuthUser, AuthToken token) {
        SingleAuthUser authUser = (SingleAuthUser) srcAuthUser;
        authUser.setUuid(token.getUuid());
        authUser.setDeviceTypeId(token.getDeviceTypeId());
        authUser.setUserType(token.getUserType());
        String json = JsonKit.toJSON(authUser);
        TimeUnit timeUnit = this.protected_getTimeunitToBeUsed().asTimeUnit();
        this.redisTemplate.opsForValue().set(key, json, token.getExpiration(), timeUnit);
    }

    @Override
    protected void removeAuthUser(String key) {
        this.redisTemplate.delete(key);
    }

    @Override
    protected void updateAuthUser(String key, AuthUser authUser, String separator) {
        String script = "local key = ARGV[1];" +
                        "local newValue = ARGV[2];" +
                        "if redis.call('exists', key) == 1 then" +
                        "    local ttl = redis.call('ttl', key);" +
                        "    redis.call('set', key, newValue);" +
                        "    if ttl > 0 then" +
                        "        redis.call('expire', key, ttl);" +
                        "    end" +
                        "    return 1;" +
                        "else" +
                        "    return 0;" +
                        "end";
        String json = JsonKit.toJSON(authUser);
        this.redisTemplate.execute((RedisConnection connection) -> connection.eval(
                script.getBytes(),
                ReturnType.INTEGER,
                0,
                key.getBytes(),
                json.getBytes()
        ));
    }
}
